package mmi.gui.shape;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import mmi.gui.Application;

/**
 * 
 * @author louis
 *
 */
public class Triangle extends Shape {

	protected Triangle(Point2D start, Point2D end) {
		super( new Point2D.Double( Math.min(start.getX(), end.getX()),	Math.min(start.getY(), end.getY())),
			   new Point2D.Double( Math.max(start.getX(), end.getX()),	Math.max(start.getY(), end.getY())));
		
		this.start = start;
		this.end = end;
		
		triangle.addPoint((int) start.getX(), (int) start.getY());
		triangle.addPoint((int) end.getX(), (int) end.getY());
		triangle.addPoint((int) middle.getX(), (int) middle.getY());
	}
	
	protected Triangle(int id, Point2D start, Point2D end) {
		super(id, new Point2D.Double( Math.min(start.getX(), end.getX()),	Math.min(start.getY(), end.getY())),
			   new Point2D.Double( Math.max(start.getX(), end.getX()),	Math.max(start.getY(), end.getY())));
		
		this.start = start;
		this.end = end;
		
		triangle.addPoint((int) start.getX(), (int) start.getY());
		triangle.addPoint((int) end.getX(), (int) end.getY());
		triangle.addPoint((int) middle.getX(), (int) middle.getY());
	}
	
	public Triangle(Point2D start, Point2D end, Point2D middle, Color lineColour, Color fillColour, int lineWidth)
	{
		super( new Point2D.Double( Math.min(start.getX(), end.getX()),	Math.min(start.getY(), end.getY())),
			   new Point2D.Double( Math.max(start.getX(), end.getX()),	Math.max(start.getY(), end.getY())),
			   lineColour, fillColour, lineWidth);

		this.start = start;
		this.end = end;
		this.middle = (middle == null) ? (new Point2D.Double()) : middle;
		
		triangle.addPoint((int) start.getX(), (int) start.getY());
		triangle.addPoint((int) end.getX(), (int) end.getY());
		triangle.addPoint((int) middle.getX(), (int) middle.getY());
	}
	
	public Triangle(int id, Point2D start, Point2D end, Point2D middle, Color lineColour, Color fillColour, int lineWidth)
	{
		super(id, new Point2D.Double( Math.min(start.getX(), end.getX()),	Math.min(start.getY(), end.getY())),
			      new Point2D.Double( Math.max(start.getX(), end.getX()),	Math.max(start.getY(), end.getY())),
			      lineColour, fillColour, lineWidth);

		this.start = start;
		this.end = end;
		this.middle = middle;
		
		triangle.addPoint((int) start.getX(), (int) start.getY());
		triangle.addPoint((int) end.getX(), (int) end.getY());
		triangle.addPoint((int) middle.getX(), (int) middle.getY());
	}

	private java.awt.Polygon triangle = new Polygon();
	
	@Override
	public boolean contains(Point2D position) {
		return isSide(position) != null || triangle.contains(position);
	}

	@Override
	public void draw(Graphics2D g) {		
		Stroke defaultStroke = g.getStroke();
		Color defaultColour = g.getColor();
			
		g.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER));
		g.setColor(lineColour);
		
		for(int i = 0, j=0; i < triangle.npoints; i++, j++)
		{
			if(j+1 >= triangle.npoints) j = -1;
			g.drawLine(triangle.xpoints[i], triangle.ypoints[i], triangle.xpoints[j+1], triangle.ypoints[j+1]);
		}
		
		g.setColor(fillColour);
		
		fill(g);
		
		g.setStroke(defaultStroke);
		g.setColor(defaultColour);
		
		drawBoundingBox(g);
	}

	@Override
	public void resizeShape(Shape.Sides side, Double optionalXDif, Double optionalYDif)
	{
		double xdif = (optionalXDif == null) ? 0 : optionalXDif;
		double ydif = (optionalYDif == null) ? 0 : optionalYDif;
		
		switch (side) {
		case BelowRight: // between second and third click
			end.setLocation(end.getX() + xdif, end.getY() + ydif);
			break;
		case BelowLeft: // third click
			middle.setLocation(middle.getX() + xdif, middle.getY() + ydif);
			break;
		case TopLeft: // first click
			start.setLocation(start.getX() + xdif, start.getY() + ydif);
			break;
		case TopRight: // between first and third click
			start.setLocation(start.getX(), start.getY() + ydif);
			middle.setLocation(middle.getX(), middle.getY() + ydif);
			break;
		case Top: // second click
			end.setLocation(end.getX() + xdif, end.getY() + ydif);
			break;
		case Right: // between first and second click
			end.setLocation(end.getX() + xdif, end.getY());
			break;
		default:
			break;
		}
		
		makeTheShape();
	}
	
	/**
	 * Draws a default bounding box. The default is to place 4 bounding boxes, in each corner one.
	 * For special bounding boxes per shape, override this function.
	 * @param g
	 */
	@Override
	public void drawBoundingBox(Graphics2D g)
	{
		if(showBoundingBox)
		{	
			// start
			g.fillRect((int) (start.getX() - 0.5*Shape.getBoundingBoxSize()), (int) (start.getY() - 0.5*Shape.getBoundingBoxSize()), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			
			// start-middle
			g.fillRect((int) ((start.getX() + middle.getX())/2 - 0.5*Shape.getBoundingBoxSize()),
					   (int) ((start.getY() + middle.getY())/2 - 0.5*Shape.getBoundingBoxSize()),
					   Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			
			// middle
			g.fillRect((int) (middle.getX() - 0.5*Shape.getBoundingBoxSize()), (int) (middle.getY() - 0.5*Shape.getBoundingBoxSize()), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			
			// middle-end
			g.fillRect((int) ((middle.getX() + end.getX())/2 - 0.5*Shape.getBoundingBoxSize()),
					   (int) ((middle.getY() + end.getY())/2 - 0.5*Shape.getBoundingBoxSize()),
					   Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			
			// end
			g.fillRect((int) (end.getX() - 0.5*Shape.getBoundingBoxSize()), (int) (end.getY() - 0.5*Shape.getBoundingBoxSize()), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			
			// end-start
			g.fillRect((int) ((end.getX() + start.getX())/2 - 0.5*Shape.getBoundingBoxSize()),
					   (int) ((end.getY() + start.getY())/2 - 0.5*Shape.getBoundingBoxSize()),
					   Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
			

			Stroke defaultStroke = g.getStroke();
			g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0));

			// start-middle line
			g.drawLine((int) start.getX(), (int) start.getY(), 
					(int) middle.getX(), (int) middle.getY());
			
			// middle-end line
			g.drawLine((int) middle.getX(), (int) middle.getY(), 
					(int) end.getX(), (int) end.getY());
			
			// end-start line
			g.drawLine((int) end.getX(), (int) end.getY(), 
					(int) start.getX(), (int) start.getY());
	
			g.setStroke(defaultStroke);
		}
	}
	
	/**
	 * Test if a position is corresponding to one of the bounding boxes
	 * @param position to test for a bounding box
	 * @return Side if position corresponds to a bounding box, otherwise null
	 */
	@Override
	public Sides isSide(Point2D position)
	{				
		Rectangle2D startBox = new Rectangle2D.Double(start.getX() - 0.5*Shape.getBoundingBoxSize(), start.getY() - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(startBox.contains(position))
		{
			Application.TraceDebug("TL side click detected");
			return Sides.TopLeft;
		}
		
		Rectangle2D startMiddleBox = new Rectangle2D.Double((start.getX() + middle.getX())/2 - 0.5*Shape.getBoundingBoxSize(), (start.getY() + middle.getY())/2 - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(startMiddleBox.contains(position))
		{
			Application.TraceDebug("TR side click detected");
			return Sides.TopRight;
		}
			
		Rectangle2D middleBox = new Rectangle2D.Double(middle.getX() - 0.5*Shape.getBoundingBoxSize(), middle.getY() - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(middleBox.contains(position))
		{
			Application.TraceDebug("BL side click detected");
			return Sides.BelowLeft;
		}
		
		Rectangle2D middleEndBox = new Rectangle2D.Double((middle.getX() + end.getX())/2 - 0.5*Shape.getBoundingBoxSize(), (middle.getY() + end.getY())/2 - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(middleEndBox.contains(position))
		{
			Application.TraceDebug("BR side click detected");
			return Sides.BelowRight;
		}
		
		Rectangle2D endBox = new Rectangle2D.Double(end.getX() - 0.5*Shape.getBoundingBoxSize(), end.getY() - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(endBox.contains(position))
		{
			Application.TraceDebug("T side click detected");
			return Sides.Top;
		}
		
		Rectangle2D endStartBox = new Rectangle2D.Double((end.getX() + start.getX())/2 - 0.5*Shape.getBoundingBoxSize(), (end.getY() + start.getY())/2 - 0.5*Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize(), Shape.getBoundingBoxSize());
		if(endStartBox.contains(position))
		{
			Application.TraceDebug("R side click detected");
			return Sides.Right;
		}
		
		return null;
	}
	
	@Override
	public void fill(Graphics2D g) {			
		Color defaultColour = g.getColor();
		if(fillColour == null)
			g.setColor(Color.white);
		else
			g.setColor(fillColour);
		g.fillPolygon(triangle);
		g.setColor(defaultColour);
	}

	@Override
	public Shapes getShape() {
		return Shape.Shapes.Triangle;
	}

	@Override
	public void makeTheShape()
	{
		triangle.reset();
		triangle.addPoint((int) start.getX(), (int) start.getY());
		triangle.addPoint((int) end.getX(), (int) end.getY());
		triangle.addPoint((int) middle.getX(), (int) middle.getY());
	}
	
	@Override
	public String getXMLRepresentation() {
		String s = "";
		
		s += "\t\t<Shape shapetype=\"Triangle\" id=\"" + id + "\">" + "\n";
		s += "\t\t\t<xstart>" + start.getX() + "</xstart>" + "\n";
		s += "\t\t\t<ystart>" + start.getY() + "</ystart>" + "\n";
		s += "\t\t\t<xmiddle>" + middle.getX() + "</xmiddle>" + "\n";
		s += "\t\t\t<ymiddle>" + middle.getY() + "</ymiddle>" + "\n";
		s += "\t\t\t<xend>" + end.getX() + "</xend>" + "\n";
		s += "\t\t\t<yend>" + end.getY() + "</yend>" + "\n";
		s += "\t\t\t<linecolour>" + lineColour + "</linecolour>" + "\n";
		s += "\t\t\t<fillcolour>" + fillColour + "</fillcolour>" + "\n";
		s += "\t\t\t<linewidth>" + lineWidth + "</linewidth>" + "\n";
		s += "\t\t</Shape>";
		
		return s;
	}

	@Override
	public String toString() {
		String s = "";
		
		s += "Triangle [" + id + "]: " + start.getX() + " " + start.getY() + " " + middle.getX() + " " + middle.getY() + " " + end.getX() + " " + end.getY();
		
		return s;
	}

}
